技术改变世界,文化改变人心

ThinkPHP-5.0.* RCE分析

前几周一直在复习考试,也没有看最近出的一些洞,寒假闲下来以后,要把最近出的洞都补了

漏洞核心

TP的这个RCE漏洞,主要的触发点在Request类中的method方法,这个存在一个当前类的任意方法调用,之后通过这个任意方法调用去覆盖掉filter的默认方法,从而实现RCE

enter description here

可以看到在526行,调用了$this->{$this->method}($_POST),在这里,因为$this->method$_POST我们都可控,可以调用当前类的任意方法,

漏洞分析

程序流程

  1. 在App.php中,在116行的$dispatch = self::routeCheck($request,$config);进入URL路由检测
  2. 通过URL检测,TP获取到执行的控制器为captcha
  3. 在以下的调用栈中获取到了请求方法的type为method

enter description here

enter description here

  1. 之后回到Route.php调用了$method = strtolower($request->method();这里就是触发点
  2. 进入$request->method()

enter description here

这里如果传入的POST数组设置了配置中的var_method的话,可以进入条件判断,我们看一下var_method是什么值

enter description here

所以,如果我们传入的POST数组中有_method键值对的话就可以直接进入逻辑

  1. 进入逻辑后,很容易看出来,$this->method的值是我们的_method的值,我们可以利用这一点来执行当前类的任意方法
  2. 在当前类的__construct()方法中,传入的是一个数组,我们可以通过_method再一次调用这个类的__construct函数
    如果这个类没有可以用来进行一些恶意操作的函数的话,这个当前类的任意方法调用并没有什么用,但是巧的是,在__construct函数中有着变量覆盖,这样的话,我们就可以覆盖整个类中的所有成员变量,我们看看__construct函数

enter description here

传入的参数是一个数组,正好我们之前的方法调用的参数正好是POST数组,而且在139行有一个很明显的变量赋值的操作,而且这个变量的键和值我们都可控,意味着我们可以进行当前类的变量覆盖,当然,显而易见的filter变量应该是最好的选择,因为filter会对所有传入的参数进行一次函数调用

enter description here

最后,我们将filter的值覆盖为系统函数system

enter description here

这里我们同时赋值$this->method的值为get,因为captcha路由规则需要是get方法才能不出错

  1. 在路由结束以后,调用了$data = self::exec($dispatch, $config);
  2. 因为在之前的路由中,判断路由的类型是method,所以进入了method的逻辑

enter description here

  1. 这里又回到了Request类的方法调用,还记得我们之前对Reuqest类的变量覆盖吗,在这一步获取了filter的值,但是在之前,我们把filter覆盖为了system

enter description here

  1. 在下面调用array_walk_recursive方法将$data的值都应用filterValue函数

enter description here

enter description here

  1. 最后,filterValue会对$data内的每个值都调用filter函数,这个时候,filter为system,data中为whoami

enter description here

最后成功实现RCE

总结

TP的此次RCE是因为对传入的_method没有进行过滤,所以TP在之后的修复中,也是对其中的_method设置了白名单

enter description here

现在的web漏洞已经不再是以前的一个SQL注入,命令执行打天下的时代了,现在web的漏洞很多都需要很长的调用链才可以将整个攻击连贯起来